﻿using System;
using System.Diagnostics.Contracts;

namespace Isuka.Iteration.Auxiliaries
{
    public interface IIteratorTraits<TSrc, TDst>
        : IEquatable<IIteratorTraits<TSrc, TDst>>
    {
        bool Filter(TSrc value);

        TDst Map(TSrc value);
    }

    public sealed class FilterTraits<TValue>
        : IIteratorTraits<TValue, TValue>
    {
        readonly Func<TValue, bool> predicate;

        public FilterTraits(Func<TValue, bool> predicate)
        {
            Contract.Requires<ArgumentNullException>(predicate != null, "predicate");

            this.predicate = predicate;
        }

        #region IIteratorTraits<TValue,TValue> メンバー

        public bool Filter(TValue value)
        {
            return predicate(value);
        }

        public TValue Map(TValue value)
        {
            return value;
        }

        #endregion

        #region IEquatable<IIteratorTraits<TValue,TValue>> メンバー

        public bool Equals(IIteratorTraits<TValue, TValue> other)
        {
            var obj = other as FilterTraits<TValue>;
            return obj != null && predicate == obj.predicate;
        }

        // override object.Equals
        public override bool Equals(object obj)
        {
            return Equals(obj as IIteratorTraits<TValue, TValue>);
        }

        // override object.GetHashCode
        public override int GetHashCode()
        {
            return predicate.GetHashCode();
        }

        #endregion
    }

    public sealed class MapTraits<TSrc, TDst>
        : IIteratorTraits<TSrc, TDst>
    {
        readonly Func<TSrc, TDst> mapper;

        public MapTraits(Func<TSrc, TDst> mapper)
        {
            Contract.Requires<ArgumentNullException>(mapper != null, "mapper");

            this.mapper = mapper;
        }

        #region IIteratorTraits<TSrc,TDst> メンバー

        public bool Filter(TSrc value)
        {
            return true;
        }

        public TDst Map(TSrc value)
        {
            return mapper(value);
        }

        #endregion

        #region IEquatable<IIteratorTraits<TSrc,TDst>> メンバー

        public bool Equals(IIteratorTraits<TSrc, TDst> other)
        {
            var obj = other as MapTraits<TSrc, TDst>;
            return obj != null && mapper == obj.mapper;
        }

        // override object.Equals
        public override bool Equals(object obj)
        {
            return Equals(obj as IIteratorTraits<TSrc, TDst>);
        }

        // override object.GetHashCode
        public override int GetHashCode()
        {
            return mapper.GetHashCode();
        }

        #endregion
    }
}
